Best time to buy and sell stock III

Time: O(N); Space: O(1); hard

Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete at most two transactions.

Note: You may not engage in multiple transactions at the same time (i.e., you must sell the stock before you buy again).

Example 1:

Input: prices = [3,3,5,0,0,3,1,4]

Output: 6

Explanation:

  • Buy on day 4 (price = 0) and sell on day 6 (price = 3), profit = 3-0 = 3.

  • Then buy on day 7 (price = 1) and sell on day 8 (price = 4), profit = 4-1 = 3.

Example 2:

Input: prices = [1,2,3,4,5]

Output: 4

Explanation:

  • Buy on day 1 (price = 1) and sell on day 5 (price = 5), profit = 5-1 = 4.

  • Note that you cannot buy on day 1, buy on day 2 and sell them later, as you are engaging multiple transactions at the same time. You must sell before buying again.

Example 3:

Input: prices = [7,6,4,3,1]

Output: 0

Explanation:

  • In this case, no transaction is done, i.e. max profit = 0.

[1]:
class Solution1(object):
    """
    Time: O(N)
    Space: O(1)
    """
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        hold1, hold2 = float("-inf"), float("-inf")
        release1, release2 = 0, 0

        for i in prices:
            release2 = max(release2, hold2 + i)
            hold2 = max(hold2, release1 - i)
            release1 = max(release1, hold1 + i)
            hold1 = max(hold1, -i)

        return release2
[2]:
s = Solution1()

prices = [3,3,5,0,0,3,1,4]
assert s.maxProfit(prices) == 6

prices = [1,2,3,4,5]
assert s.maxProfit(prices) == 4

prices = [7,6,4,3,1]
assert s.maxProfit(prices) == 0
[5]:
class Solution2(object):

    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        return self.maxAtMostKPairsProfit(prices, 2)

    def maxAtMostKPairsProfit(self, prices, k):
        max_buy = [float("-inf") for _ in range(k + 1)]
        max_sell = [0 for _ in range(k + 1)]

        for i in range(len(prices)):
            for j in range(1, min(k, i//2 + 1) + 1):
                max_buy[j] = max(max_buy[j], max_sell[j-1] - prices[i])
                max_sell[j] = max(max_sell[j], max_buy[j] + prices[i])

        return max_sell[k]
[6]:
s = Solution2()

prices = [3,3,5,0,0,3,1,4]
assert s.maxProfit(prices) == 6

prices = [1,2,3,4,5]
assert s.maxProfit(prices) == 4

prices = [7,6,4,3,1]
assert s.maxProfit(prices) == 0
[7]:
class Solution3(object):
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        min_price, max_profit_from_left, max_profits_from_left = float("inf"), 0, []

        for price in prices:
            min_price = min(min_price, price)
            max_profit_from_left = max(max_profit_from_left, price - min_price)
            max_profits_from_left.append(max_profit_from_left)

        max_price, max_profit_from_right, max_profits_from_right = 0, 0, []

        for i in reversed(range(len(prices))):
            max_price = max(max_price, prices[i])
            max_profit_from_right = max(max_profit_from_right,
                                        max_price - prices[i])
            max_profits_from_right.insert(0, max_profit_from_right)

        max_profit = 0

        for i in range(len(prices)):
            max_profit = max(max_profit,
                             max_profits_from_left[i] +
                             max_profits_from_right[i])

        return max_profit
[8]:
s = Solution3()

prices = [3,3,5,0,0,3,1,4]
assert s.maxProfit(prices) == 6

prices = [1,2,3,4,5]
assert s.maxProfit(prices) == 4

prices = [7,6,4,3,1]
assert s.maxProfit(prices) == 0